/**
 * echarts图表类：漏斗图
 *
 * @desc echarts基于Canvas，纯Javascript图表库，提供直观，生动，可交互，可个性化定制的数据统计图表。
 * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *
 */


var ChartBase = require('./base');
// 图形依赖
var TextShape = require('zrender/shape/Text');
var LineShape = require('zrender/shape/Line');
var PolygonShape = require('zrender/shape/Polygon');
var ecConfig = require('../config');
// 漏斗图默认参数
ecConfig.funnel = {
    zlevel: 0,
    // 一级层叠
    z: 2,
    // 二级层叠
    clickable: true,
    legendHoverLink: true,
    x: 80,
    y: 60,
    x2: 80,
    y2: 60,
    // width: {totalWidth} - x - x2,
    // height: {totalHeight} - y - y2,
    min: 0,
    max: 100,
    minSize: '0%',
    maxSize: '100%',
    sort: 'descending',
    // 'ascending', 'descending'
    gap: 0,
    funnelAlign: 'center',
    itemStyle: {
        normal: {
            // color: 各异,
            borderColor: '#fff',
            borderWidth: 1,
            label: {
                show: true,
                position: 'outer'    // formatter: 标签文本格式器，同Tooltip.formatter，不支持异步回调
                           // textStyle: null      // 默认使用全局文本样式，详见TEXTSTYLE
            },
            labelLine: {
                show: true,
                length: 10,
                lineStyle: {
                    // color: 各异,
                    width: 1,
                    type: 'solid'
                }
            }
        },
        emphasis: {
            // color: 各异,
            borderColor: 'rgba(0,0,0,0)',
            borderWidth: 1,
            label: { show: true },
            labelLine: { show: true }
        }
    }
};
var ecData = require('../util/ecData');
var number = require('../util/number');
var zrUtil = require('zrender/tool/util');
var zrColor = require('zrender/tool/color');
var zrArea = require('zrender/tool/area');
/**
     * 构造函数
     * @param {Object} messageCenter echart消息中心
     * @param {ZRender} zr zrender实例
     * @param {Object} series 数据
     * @param {Object} component 组件
     */
function Funnel(ecTheme, messageCenter, zr, option, myChart) {
    // 图表基类
    ChartBase.call(this, ecTheme, messageCenter, zr, option, myChart);
    this.refresh(option);
}
Funnel.prototype = {
    type: ecConfig.CHART_TYPE_FUNNEL,
    /**
         * 绘制图形
         */
    _buildShape: function () {
        var series = this.series;
        var legend = this.component.legend;
        // 复用参数索引
        this._paramsMap = {};
        this._selected = {};
        this.selectedMap = {};
        var serieName;
        for (var i = 0, l = series.length; i < l; i++) {
            if (series[i].type === ecConfig.CHART_TYPE_FUNNEL) {
                series[i] = this.reformOption(series[i]);
                this.legendHoverLink = series[i].legendHoverLink || this.legendHoverLink;
                serieName = series[i].name || '';
                // 系列图例开关
                this.selectedMap[serieName] = legend ? legend.isSelected(serieName) : true;
                if (!this.selectedMap[serieName]) {
                    continue;
                }
                this._buildSingleFunnel(i);
                this.buildMark(i);
            }
        }
        this.addShapeList();
    },
    /**
         * 构建单个仪表盘
         *
         * @param {number} seriesIndex 系列索引
         */
    _buildSingleFunnel: function (seriesIndex) {
        var legend = this.component.legend;
        var serie = this.series[seriesIndex];
        var data = this._mapData(seriesIndex);
        var location = this._getLocation(seriesIndex);
        this._paramsMap[seriesIndex] = {
            location: location,
            data: data
        };
        var itemName;
        var total = 0;
        var selectedData = [];
        // 计算需要显示的个数和总值
        for (var i = 0, l = data.length; i < l; i++) {
            itemName = data[i].name;
            this.selectedMap[itemName] = legend ? legend.isSelected(itemName) : true;
            if (this.selectedMap[itemName] && !isNaN(data[i].value)) {
                selectedData.push(data[i]);
                total++;
            }
        }
        if (total === 0) {
            return;
        }
        // 可计算箱子
        var funnelCase = this._buildFunnelCase(seriesIndex);
        var align = serie.funnelAlign;
        var gap = serie.gap;
        var height = total > 1 ? (location.height - (total - 1) * gap) / total : location.height;
        var width;
        var lastY = location.y;
        var lastWidth = serie.sort === 'descending' ? this._getItemWidth(seriesIndex, selectedData[0].value) : number.parsePercent(serie.minSize, location.width);
        var next = serie.sort === 'descending' ? 1 : 0;
        var centerX = location.centerX;
        var pointList = [];
        var x;
        var polygon;
        var lastPolygon;
        for (var i = 0, l = selectedData.length; i < l; i++) {
            itemName = selectedData[i].name;
            if (this.selectedMap[itemName] && !isNaN(selectedData[i].value)) {
                width = i <= l - 2 ? this._getItemWidth(seriesIndex, selectedData[i + next].value) : serie.sort === 'descending' ? number.parsePercent(serie.minSize, location.width) : number.parsePercent(serie.maxSize, location.width);
                switch (align) {
                case 'left':
                    x = location.x;
                    break;
                case 'right':
                    x = location.x + location.width - lastWidth;
                    break;
                default:
                    x = centerX - lastWidth / 2;
                }
                polygon = this._buildItem(seriesIndex, selectedData[i]._index, legend    // color
 ? legend.getColor(itemName) : this.zr.getColor(selectedData[i]._index), x, lastY, lastWidth, width, height, align);
                lastY += height + gap;
                lastPolygon = polygon.style.pointList;
                pointList.unshift([
                    lastPolygon[0][0] - 10,
                    lastPolygon[0][1]
                ]);
                // 左
                pointList.push([
                    lastPolygon[1][0] + 10,
                    lastPolygon[1][1]
                ]);
                // 右
                if (i === 0) {
                    if (lastWidth === 0) {
                        lastPolygon = pointList.pop();
                        align == 'center' && (pointList[0][0] += 10);
                        align == 'right' && (pointList[0][0] = lastPolygon[0]);
                        pointList[0][1] -= align == 'center' ? 10 : 15;
                        if (l == 1) {
                            lastPolygon = polygon.style.pointList;
                        }
                    } else {
                        pointList[pointList.length - 1][1] -= 5;
                        pointList[0][1] -= 5;
                    }
                }
                lastWidth = width;
            }
        }
        if (funnelCase) {
            pointList.unshift([
                lastPolygon[3][0] - 10,
                lastPolygon[3][1]
            ]);
            // 左
            pointList.push([
                lastPolygon[2][0] + 10,
                lastPolygon[2][1]
            ]);
            // 右
            if (lastWidth === 0) {
                lastPolygon = pointList.pop();
                align == 'center' && (pointList[0][0] += 10);
                align == 'right' && (pointList[0][0] = lastPolygon[0]);
                pointList[0][1] += align == 'center' ? 10 : 15;
            } else {
                pointList[pointList.length - 1][1] += 5;
                pointList[0][1] += 5;
            }
            funnelCase.style.pointList = pointList;
        }
    },
    _buildFunnelCase: function (seriesIndex) {
        var serie = this.series[seriesIndex];
        if (this.deepQuery([
                serie,
                this.option
            ], 'calculable')) {
            var location = this._paramsMap[seriesIndex].location;
            var gap = 10;
            var funnelCase = {
                hoverable: false,
                style: {
                    pointListd: [
                        [
                            location.x - gap,
                            location.y - gap
                        ],
                        [
                            location.x + location.width + gap,
                            location.y - gap
                        ],
                        [
                            location.x + location.width + gap,
                            location.y + location.height + gap
                        ],
                        [
                            location.x - gap,
                            location.y + location.height + gap
                        ]
                    ],
                    brushType: 'stroke',
                    lineWidth: 1,
                    strokeColor: serie.calculableHolderColor || this.ecTheme.calculableHolderColor || ecConfig.calculableHolderColor
                }
            };
            ecData.pack(funnelCase, serie, seriesIndex, undefined, -1);
            this.setCalculable(funnelCase);
            funnelCase = new PolygonShape(funnelCase);
            this.shapeList.push(funnelCase);
            return funnelCase;
        }
    },
    _getLocation: function (seriesIndex) {
        var gridOption = this.series[seriesIndex];
        var zrWidth = this.zr.getWidth();
        var zrHeight = this.zr.getHeight();
        var x = this.parsePercent(gridOption.x, zrWidth);
        var y = this.parsePercent(gridOption.y, zrHeight);
        var width = gridOption.width == null ? zrWidth - x - this.parsePercent(gridOption.x2, zrWidth) : this.parsePercent(gridOption.width, zrWidth);
        return {
            x: x,
            y: y,
            width: width,
            height: gridOption.height == null ? zrHeight - y - this.parsePercent(gridOption.y2, zrHeight) : this.parsePercent(gridOption.height, zrHeight),
            centerX: x + width / 2
        };
    },
    _mapData: function (seriesIndex) {
        var serie = this.series[seriesIndex];
        var funnelData = zrUtil.clone(serie.data);
        for (var i = 0, l = funnelData.length; i < l; i++) {
            funnelData[i]._index = i;
        }
        function numDescending(a, b) {
            if (a.value === '-') {
                return 1;
            } else if (b.value === '-') {
                return -1;
            }
            return b.value - a.value;
        }
        function numAscending(a, b) {
            return -numDescending(a, b);
        }
        if (serie.sort != 'none') {
            funnelData.sort(serie.sort === 'descending' ? numDescending : numAscending);
        }
        return funnelData;
    },
    /**
         * 构建单个扇形及指标
         */
    _buildItem: function (seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align) {
        var series = this.series;
        var serie = series[seriesIndex];
        var data = serie.data[dataIndex];
        // 漏斗
        var polygon = this.getPolygon(seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align);
        ecData.pack(polygon, series[seriesIndex], seriesIndex, series[seriesIndex].data[dataIndex], dataIndex, series[seriesIndex].data[dataIndex].name);
        this.shapeList.push(polygon);
        // 文本标签
        var label = this.getLabel(seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align);
        ecData.pack(label, series[seriesIndex], seriesIndex, series[seriesIndex].data[dataIndex], dataIndex, series[seriesIndex].data[dataIndex].name);
        this.shapeList.push(label);
        // 特定状态下是否需要显示文本标签
        if (!this._needLabel(serie, data, false)) {
            label.invisible = true;
        }
        // 文本标签视觉引导线
        var labelLine = this.getLabelLine(seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align);
        this.shapeList.push(labelLine);
        // 特定状态下是否需要显示文本标签引导线
        if (!this._needLabelLine(serie, data, false)) {
            labelLine.invisible = true;
        }
        var polygonHoverConnect = [];
        var labelHoverConnect = [];
        if (this._needLabelLine(serie, data, true)) {
            polygonHoverConnect.push(labelLine.id);
            labelHoverConnect.push(labelLine.id);
        }
        if (this._needLabel(serie, data, true)) {
            polygonHoverConnect.push(label.id);
            labelHoverConnect.push(polygon.id);
        }
        polygon.hoverConnect = polygonHoverConnect;
        label.hoverConnect = labelHoverConnect;
        return polygon;
    },
    /**
         * 根据值计算宽度 
         */
    _getItemWidth: function (seriesIndex, value) {
        var serie = this.series[seriesIndex];
        var location = this._paramsMap[seriesIndex].location;
        var min = serie.min;
        var max = serie.max;
        var minSize = number.parsePercent(serie.minSize, location.width);
        var maxSize = number.parsePercent(serie.maxSize, location.width);
        return (value - min) * (maxSize - minSize) / (max - min) + minSize;
    },
    /**
         * 构建扇形
         */
    getPolygon: function (seriesIndex, dataIndex, defaultColor, xLT, y, topWidth, bottomWidth, height, align) {
        var serie = this.series[seriesIndex];
        var data = serie.data[dataIndex];
        var queryTarget = [
            data,
            serie
        ];
        // 多级控制
        var normal = this.deepMerge(queryTarget, 'itemStyle.normal') || {};
        var emphasis = this.deepMerge(queryTarget, 'itemStyle.emphasis') || {};
        var normalColor = this.getItemStyleColor(normal.color, seriesIndex, dataIndex, data) || defaultColor;
        var emphasisColor = this.getItemStyleColor(emphasis.color, seriesIndex, dataIndex, data) || (typeof normalColor === 'string' ? zrColor.lift(normalColor, -0.2) : normalColor);
        var xLB;
        switch (align) {
        case 'left':
            xLB = xLT;
            break;
        case 'right':
            xLB = xLT + (topWidth - bottomWidth);
            break;
        default:
            xLB = xLT + (topWidth - bottomWidth) / 2;
            break;
        }
        var polygon = {
            zlevel: serie.zlevel,
            z: serie.z,
            clickable: this.deepQuery(queryTarget, 'clickable'),
            style: {
                pointList: [
                    [
                        xLT,
                        y
                    ],
                    [
                        xLT + topWidth,
                        y
                    ],
                    [
                        xLB + bottomWidth,
                        y + height
                    ],
                    [
                        xLB,
                        y + height
                    ]
                ],
                brushType: 'both',
                color: normalColor,
                lineWidth: normal.borderWidth,
                strokeColor: normal.borderColor
            },
            highlightStyle: {
                color: emphasisColor,
                lineWidth: emphasis.borderWidth,
                strokeColor: emphasis.borderColor
            }
        };
        if (this.deepQuery([
                data,
                serie,
                this.option
            ], 'calculable')) {
            this.setCalculable(polygon);
            polygon.draggable = true;
        }
        return new PolygonShape(polygon);
    },
    /**
         * 需要显示则会有返回构建好的shape，否则返回undefined
         */
    getLabel: function (seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align) {
        var serie = this.series[seriesIndex];
        var data = serie.data[dataIndex];
        var location = this._paramsMap[seriesIndex].location;
        // serie里有默认配置，放心大胆的用！
        var itemStyle = zrUtil.merge(zrUtil.clone(data.itemStyle) || {}, serie.itemStyle);
        var status = 'normal';
        // label配置
        var labelControl = itemStyle[status].label;
        var textStyle = labelControl.textStyle || {};
        var lineLength = itemStyle[status].labelLine.length;
        var text = this.getLabelText(seriesIndex, dataIndex, status);
        var textFont = this.getFont(textStyle);
        var textAlign;
        var textColor = defaultColor;
        labelControl.position = labelControl.position || itemStyle.normal.label.position;
        if (labelControl.position === 'inner' || labelControl.position === 'inside' || labelControl.position === 'center') {
            // 内部
            textAlign = align;
            textColor = Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont) ? '#fff' : zrColor.reverse(defaultColor);
        } else if (labelControl.position === 'left') {
            // 左侧显示
            textAlign = 'right';
        } else {
            // 右侧显示，默认 labelControl.position === 'outer' || 'right)
            textAlign = 'left';
        }
        var textShape = {
            zlevel: serie.zlevel,
            z: serie.z + 1,
            style: {
                x: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),
                y: y + height / 2,
                color: textStyle.color || textColor,
                text: text,
                textAlign: textStyle.align || textAlign,
                textBaseline: textStyle.baseline || 'middle',
                textFont: textFont
            }
        };
        //----------高亮
        status = 'emphasis';
        // label配置
        labelControl = itemStyle[status].label || labelControl;
        textStyle = labelControl.textStyle || textStyle;
        lineLength = itemStyle[status].labelLine.length || lineLength;
        labelControl.position = labelControl.position || itemStyle.normal.label.position;
        text = this.getLabelText(seriesIndex, dataIndex, status);
        textFont = this.getFont(textStyle);
        textColor = defaultColor;
        if (labelControl.position === 'inner' || labelControl.position === 'inside' || labelControl.position === 'center') {
            // 内部
            textAlign = align;
            textColor = Math.max(topWidth, bottomWidth) / 2 > zrArea.getTextWidth(text, textFont) ? '#fff' : zrColor.reverse(defaultColor);
        } else if (labelControl.position === 'left') {
            // 左侧显示
            textAlign = 'right';
        } else {
            // 右侧显示，默认 labelControl.position === 'outer' || 'right)
            textAlign = 'left';
        }
        textShape.highlightStyle = {
            x: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),
            color: textStyle.color || textColor,
            text: text,
            textAlign: textStyle.align || textAlign,
            textFont: textFont,
            brushType: 'fill'
        };
        return new TextShape(textShape);
    },
    /**
         * 根据lable.format计算label text
         */
    getLabelText: function (seriesIndex, dataIndex, status) {
        var series = this.series;
        var serie = series[seriesIndex];
        var data = serie.data[dataIndex];
        var formatter = this.deepQuery([
            data,
            serie
        ], 'itemStyle.' + status + '.label.formatter');
        if (formatter) {
            if (typeof formatter === 'function') {
                return formatter.call(this.myChart, {
                    seriesIndex: seriesIndex,
                    seriesName: serie.name || '',
                    series: serie,
                    dataIndex: dataIndex,
                    data: data,
                    name: data.name,
                    value: data.value
                });
            } else if (typeof formatter === 'string') {
                formatter = formatter.replace('{a}', '{a0}').replace('{b}', '{b0}').replace('{c}', '{c0}').replace('{a0}', serie.name).replace('{b0}', data.name).replace('{c0}', data.value);
                return formatter;
            }
        } else {
            return data.name;
        }
    },
    /**
         * 需要显示则会有返回构建好的shape，否则返回undefined
         */
    getLabelLine: function (seriesIndex, dataIndex, defaultColor, x, y, topWidth, bottomWidth, height, align) {
        var serie = this.series[seriesIndex];
        var data = serie.data[dataIndex];
        var location = this._paramsMap[seriesIndex].location;
        // serie里有默认配置，放心大胆的用！
        var itemStyle = zrUtil.merge(zrUtil.clone(data.itemStyle) || {}, serie.itemStyle);
        var status = 'normal';
        // labelLine配置
        var labelLineControl = itemStyle[status].labelLine;
        var lineLength = itemStyle[status].labelLine.length;
        var lineStyle = labelLineControl.lineStyle || {};
        var labelControl = itemStyle[status].label;
        labelControl.position = labelControl.position || itemStyle.normal.label.position;
        var lineShape = {
            zlevel: serie.zlevel,
            z: serie.z + 1,
            hoverable: false,
            style: {
                xStart: this._getLabelLineStartPoint(x, location, topWidth, bottomWidth, align),
                yStart: y + height / 2,
                xEnd: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),
                yEnd: y + height / 2,
                strokeColor: lineStyle.color || defaultColor,
                lineType: lineStyle.type,
                lineWidth: lineStyle.width
            }
        };
        status = 'emphasis';
        // labelLine配置
        labelLineControl = itemStyle[status].labelLine || labelLineControl;
        lineLength = itemStyle[status].labelLine.length || lineLength;
        lineStyle = labelLineControl.lineStyle || lineStyle;
        labelControl = itemStyle[status].label || labelControl;
        labelControl.position = labelControl.position;
        lineShape.highlightStyle = {
            xEnd: this._getLabelPoint(labelControl.position, x, location, topWidth, bottomWidth, lineLength, align),
            strokeColor: lineStyle.color || defaultColor,
            lineType: lineStyle.type,
            lineWidth: lineStyle.width
        };
        return new LineShape(lineShape);
    },
    _getLabelPoint: function (position, x, location, topWidth, bottomWidth, lineLength, align) {
        position = position === 'inner' || position === 'inside' ? 'center' : position;
        switch (position) {
        case 'center':
            return align == 'center' ? x + topWidth / 2 : align == 'left' ? x + 10 : x + topWidth - 10;
        case 'left':
            // 左侧文本
            if (lineLength === 'auto') {
                return location.x - 10;
            } else {
                return align == 'center'    // 居中布局
 ? location.centerX - Math.max(topWidth, bottomWidth) / 2 - lineLength : align == 'right'    // 右对齐布局
 ? x - (topWidth < bottomWidth ? bottomWidth - topWidth : 0) - lineLength : location.x - lineLength;
            }
            break;
        default:
            // 右侧文本
            if (lineLength === 'auto') {
                return location.x + location.width + 10;
            } else {
                return align == 'center'    // 居中布局
 ? location.centerX + Math.max(topWidth, bottomWidth) / 2 + lineLength : align == 'right'    // 右对齐布局
 ? location.x + location.width + lineLength : x + Math.max(topWidth, bottomWidth) + lineLength;
            }
        }
    },
    _getLabelLineStartPoint: function (x, location, topWidth, bottomWidth, align) {
        return align == 'center' ? location.centerX : topWidth < bottomWidth ? x + Math.min(topWidth, bottomWidth) / 2 : x + Math.max(topWidth, bottomWidth) / 2;
    },
    /**
         * 返回特定状态（normal or emphasis）下是否需要显示label标签文本
         * @param {Object} serie
         * @param {Object} data
         * @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
         */
    _needLabel: function (serie, data, isEmphasis) {
        return this.deepQuery([
            data,
            serie
        ], 'itemStyle.' + (isEmphasis ? 'emphasis' : 'normal') + '.label.show');
    },
    /**
         * 返回特定状态（normal or emphasis）下是否需要显示labelLine标签视觉引导线
         * @param {Object} serie
         * @param {Object} data
         * @param {boolean} isEmphasis true is 'emphasis' and false is 'normal'
         */
    _needLabelLine: function (serie, data, isEmphasis) {
        return this.deepQuery([
            data,
            serie
        ], 'itemStyle.' + (isEmphasis ? 'emphasis' : 'normal') + '.labelLine.show');
    },
    /**
         * 刷新
         */
    refresh: function (newOption) {
        if (newOption) {
            this.option = newOption;
            this.series = newOption.series;
        }
        this.backupShapeList();
        this._buildShape();
    }
};
zrUtil.inherits(Funnel, ChartBase);
// 图表注册
require('../chart').define('funnel', Funnel);
module.exports = Funnel || module.exports;;